<?php

// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 流年 <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用公共文件
define('DS', DIRECTORY_SEPARATOR);

/**
 * 
 * @param type $status
 * @param type $info
 * @return type
 * @version 2018-4-10
 */
function fmt_result($status, $info, $code = '') {
    return ['status' => $status, 'info' => $info, 'code' => $code];
}

/**
 * 应用模块 公共函数库
 * @version 2018-1-23
 */

/**
 * 把返回的数据集转换成Tree
 * @param array $list 要转换的数据集
 * @param string $pid parent标记字段
 * @param string $level level标记字段
 * @return array
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0) {
// 创建Tree
    $tree = array();
    if (is_array($list)) {
// 创建基于主键的数组引用
        $refer = array();
        foreach ($list as $key => $data) {
            $refer[$data[$pk]] = & $list[$key];
        }
        foreach ($list as $key => $data) {
// 判断是否存在parent
            $parentId = $data[$pid];
            if ($root == $parentId) {
                $tree[] = & $list[$key];
            } else {
                if (isset($refer[$parentId])) {
                    $parent = & $refer[$parentId];
                    $parent[$child][] = & $list[$key];
                }
            }
        }
    }
    return $tree;
}

/**
 * 将list_to_tree的树还原成列表
 * @param  array $tree  原来的树
 * @param  string $child 孩子节点的键
 * @param  string $order 排序显示的键，一般是主键 升序排列
 * @param  array  $list  过渡用的中间数组，
 * @return array        返回排过序的列表数组
 * @author yangweijie <yangweijiester@gmail.com>
 */
function tree_to_list($tree, $child = '_child', $order = 'id', &$list = array()) {
    if (is_array($tree)) {
        $refer = array();
        foreach ($tree as $key => $value) {
            $reffer = $value;
            if (isset($reffer[$child])) {
                unset($reffer[$child]);
                tree_to_list($value[$child], $child, $order, $list);
            }
            $list[] = $reffer;
        }
        $list = list_sort_by($list, $order, $sortby = 'asc');
    }
    return $list;
}

/**
 * 格式化字节大小
 * @param  number $size      字节数
 * @param  string $delimiter 数字和单位分隔符
 * @return string            格式化后的带单位的大小
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function format_bytes($size, $delimiter = '') {
    $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
    for ($i = 0; $size >= 1024 && $i < 5; $i++)
        $size /= 1024;
    return round($size, 2) . $delimiter . $units[$i];
}

/**
 * 把带单位的字节大小转换为字节
 * @param type $size
 * @return type
 * @version 2018-1-23 SoChishun Added.
 */
function format_to_bytes($size) {
    $units = array('B' => 1, 'KB' => 1024, 'MB' => 1048576, 'GB' => 1073741824);
    $unit = strtoupper(substr($size, -2));
    if (array_key_exists($unit, $units)) {
        return doubleval($size) * $units[$unit];
    }
    return $size;
}

/**
 * 设置跳转页面URL
 * 使用函数再次封装，方便以后选择不同的存储方式（目前使用cookie存储）
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function set_redirect_url($url) {
    cookie('redirect_url', $url);
}

/**
 * 获取跳转页面URL
 * @return string 跳转页URL
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function get_redirect_url() {
    $url = cookie('redirect_url');
    return empty($url) ? __APP__ : $url;
}

/**
 * 时间戳格式化
 * @param int $time
 * @return string 完整的时间显示
 * @author huajie <banhuajie@163.com>
 * @version 1.1 <2015-8-6> SoChishun 新增字符串时间转换功能
 */
function time_format($time = NULL, $format = 'Y-m-d H:i') {
    if (!is_numeric($time)) {
        $time = strtotime($time);
    }
    $time = $time === NULL ? NOW_TIME : intval($time);
    return date($format, $time);
}

//基于数组创建目录和文件
function create_dir_or_files($files) {
    foreach ($files as $key => $value) {
        if (substr($value, -1) == '/') {
            mkdir($value);
        } else {
            @file_put_contents($value, '');
        }
    }
}

if (!function_exists('array_column')) {

    function array_column(array $input, $columnKey, $indexKey = null) {
        $result = array();
        if (null === $indexKey) {
            if (null === $columnKey) {
                $result = array_values($input);
            } else {
                foreach ($input as $row) {
                    $result[] = $row[$columnKey];
                }
            }
        } else {
            if (null === $columnKey) {
                foreach ($input as $row) {
                    $result[$row[$indexKey]] = $row;
                }
            } else {
                foreach ($input as $row) {
                    $result[$row[$indexKey]] = $row[$columnKey];
                }
            }
        }
        return $result;
    }

}

// =========================================================================================
// @author   sutroon <14507247@qq.com>
// @version 1.0 2014-6-24 by sutroon; 2.0 2014-12-22 by sutroon 重构函数命名,统一加前缀xsfn_
// =========================================================================================
/**
 * 发送sokcet数据
 * @param string $in
 * @param string $ip
 * @param int $port
 * @return string
 * @version 1.0 2014-08-14 by kerry
 * @example socket_send($tsid . ',' . $state)
 */
function xsfn_socket_send($in, $ip = '', $port = 8855) {
    if (!$ip) {
        $ip = $_SERVER['SERVER_NAME'];
    }
    error_reporting(E_ALL);
    set_time_limit(0);
    $fp = stream_socket_client("udp://$ip:$port", $errno, $errstr);
// echo 'stream_socket_client("udp://'.$ip.':'.$port.'", $errno, $errstr)';
    if (!$fp) {
        return "ERROR: $errno - $errstr";
    } else {
        fwrite($fp, $in);
        return fread($fp, 26);
        fclose($fp);
    }
}

/**
 * 合并数组为字符串
 * 
 * @param string $glue 合并符号
 * @param array $arr 数组
 * @param int $start 开始索引
 * @param int $end 结束索引
 * @return string
 * @version 1.0 2015-2-7 by sutroon
 * @example xsfn_array_implode('|', $item, 4)
 */
function xsfn_array_implode($glue, $arr, $start = 0, $end = 0) {
    if ($end > 0) {
        $len = count($arr);
        if ($end >= $len) {
            $end = $len - 1;
        }
    }
    if ($start > 0 && $end > 0 && $start > $end) {
        $start = $end;
    }
    $i = 0;
    $str = '';
    foreach ($arr as $key => $value) {
        if ($start > 0 && $i < $start) {
            $i++;
            continue;
        }
        if ($end > 0 && $i > $end) {
            break;
        }
        $str .= $glue . $value;
        $i++;
    }
    return $str ? substr($str, 1) : $str;
}

/**
 * 字符串截取，支持中文和其他编码
 * @param string $str 需要转换的字符串
 * @param string $start 开始位置
 * @param string $length 截取长度 默认null
 * @param string $suffix 截断显示字符 默认false
 * @param string $charset 编码格式 默认utf-8
 * @return string
 * @version 1.0 <2015-4-3> SoChishun Refactoring by \Org\Util\String::msubstr
 * @version 1.1 <2015-4-21> SoChishun 新增缩略号自动长度判断逻辑
 */
function xsfn_msubstr($str, $start = 0, $length = NULL, $suffix = false, $charset = "utf-8") {
    if (function_exists("mb_substr"))
        $slice = mb_substr($str, $start, is_null($length) ? mb_strlen($str, $charset) - $start : $length, $charset);
    elseif (function_exists('iconv_substr')) {
        $slice = iconv_substr($str, $start, is_null($length) ? iconv_strlen($str, $charset) - $start : $length, $charset);
    } else {
        $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $slice = join("", array_slice($match[0], $start, $length));
    }
    if ($suffix && strlen($slice) < strlen($str)) {
        $slice .= true === $suffix ? '...' : $suffix;
    }
    return $slice;
}

/**
 * 绝对路径转相对路径
 * @param string $path
 * @return string
 * @version 1.0 <2015-10-9> SoChishun Added.
 */
function xsfn_path_ator($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    $path = substr($path, strlen($root));
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
    }
    return $path;
}

/**
 * 相对路径转绝对路径
 * @param string $path
 * @return string
 * @version 1.0 <2015-10-9> SoChishun Added.
 */
function xsfn_path_rtoa($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
    }
    return $root . $path;
}

/**
 * 重命名路径中的文件名
 * <br />用户生成缩略图路径
 * @param type $path
 * @param type $prefix
 * @param type $sufix
 * @return type
 * @version 1.0 <2015-11-02> SoChishun Added.
 */
function xsfn_rename_file($path, $prefix = 's_', $sufix = '') {
    $pathinfo = pathinfo($path);
    return $pathinfo['dirname'] . '/' . $prefix . $pathinfo['filename'] . $sufix . '.' . $pathinfo['extension'];
}

/**
 * 返回指定路径下的内容
 * @param string $directory 相对路径
 * @param array $config 选项
 * @return array
 * @throws Exception
 * @version 1.0 <2015-5-11> SoChishun Added.
 * @version 1.1 <2015-10-8> SoChishun 新增filetype文件类别属性
 */
function xsfn_read_dir($directory, $options = array()) {
    $config = array('name' => true, 'path' => true, 'real_path' => true, 'relative_path' => false, 'exten' => false, 'ctime' => false, 'mtime' => false, 'size' => false, 'is_dir' => true, 'is_file' => false, 'is_link' => false, 'is_executable' => false, 'is_readable' => false, 'is_writable' => false, 'filetype' => false);
    if ($options) {
        $config = array_merge($config, $options);
    }
    try {
        $dir = new DirectoryIterator(xsfn_path_rtoa($directory));
    } catch (Exception $e) {
        throw new Exception($directory . ' is not readable');
    }
    // 文件分类
    $filetypes = array('zip' => array('zip', 'rar', '7-zip', 'tar', 'gz', 'gzip'), 'doc' => array('txt', 'rtf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'et'), 'script' => array('php', 'js', 'css', 'c'), 'image' => array('jpg', 'jpeg', 'png', 'gif', 'tiff', 'psd', 'bmp', 'ico'));
    $files = array();
    foreach ($dir as $file) {
        if ($file->isDot()) {
            continue;
        }
        if ($config['name']) {
            $item['name'] = $file->getFileName();
        }
        if ($config['path']) {
            $item['path'] = $file->getPath();
        }
        if ($config['real_path']) {
            $item['real_path'] = $file->getRealPath();
        }
        if ($config['relative_path']) {
            $item['relative_path'] = xsfn_path_ator($file->getRealPath());
        }
        $exten = $file->getExtension();
        if ($config['exten']) {
            $item['exten'] = $exten;
        }
        if ($config['filetype']) {
            $scategory = '';
            foreach ($filetypes as $catetory => $extens) {
                if (in_array($exten, $extens)) {
                    $scategory = $catetory;
                    break;
                }
            }
            $item['filetype'] = $scategory;
        }
        if ($config['mtime']) {
            $item['mtime'] = $file->getMTime();
        }
        if ($config['ctime']) {
            $item['ctime'] = $file->getCTime();
        }
        if ($config['size']) {
            $item['size'] = $file->getSize();
        }
        if ($config['is_dir']) {
            $item['is_dir'] = $file->isDir();
        }
        if ($config['is_file']) {
            $item['is_file'] = $file->isFile();
        }
        if ($config['is_link']) {
            $item['is_link'] = $file->isLink();
        }
        if ($config['is_executable']) {
            $item['is_executable'] = $file->isExecutable();
        }
        if ($config['is_readable']) {
            $item['is_readable'] = $file->isReadable();
        }
        if ($config['is_writable']) {
            $item['is_writable'] = $file->isWritable();
        }
        $files[] = $item;
    }
    return $files;
}

/**
 * 将秒钟数格式化为时分秒的时间
 * @param int $seconds 秒钟数
 * @return string 时分秒时间格式
 * @version 1.0 2014-9-12 by sutroon
 * @example xsfn_seconds_format(61) // 00:01:01
 */
function xsfn_seconds_format($seconds) {
    if (!$seconds) {
        return '00:00:00';
    }
    if (!is_numeric($seconds)) {
        return $seconds;
    }
    $h = intval($seconds / 3600);
    $m = intval(($seconds % 3600) / 60);
    $s = $seconds % 60;

    return substr('0' . $h, -2) . ':' . substr('0' . $m, -2) . ':' . substr('0' . $s, -2);
}

/**
 * 时间轴开发，即显示为“刚刚”、“5分钟前”、“昨天10:23”等
 * @param string $time
 * @return string
 * @version 1.0.0 2013-5-4 sutroon
 * @example xsfn_tran_time('2013-5-4 13:23:33');
 */
function xsfn_tran_time($time) {
    if (!is_numeric($time)) {
        $time = strtotime($time);
    }
    $rtime = date("m-d H:i", $time);
    $htime = date("H:i", $time);
    $time = time() - $time;
    if ($time < 60) {
        $str = '刚刚';
    } elseif ($time < 60 * 60) {
        $min = floor($time / 60);
        $str = $min . '分钟前';
    } elseif ($time < 60 * 60 * 24) {
        $h = floor($time / (60 * 60));
        $str = $h . '小时前 ' . $htime;
    } elseif ($time < 60 * 60 * 24 * 3) {
        $d = floor($time / (60 * 60 * 24));
        if ($d == 1) {
            $str = '昨天 ' . $rtime;
        } else {
            $str = '前天 ' . $rtime;
        }
    } else {
        $str = $rtime;
    }
    return $str;
}

/**
 * 计算时间差
 * @param string $time1 时间1
 * @param string $time2 时间2
 * @param string $format 格式
 * @return string
 * @version 1.0 <2015-4-24> SoChishun Added.
 * @example xsfn_date_diff('2015-4-24 10:20:30','2015-4-24 17:42:33','h:i');
 */
function xsfn_date_diff($time1, $time2, $format = 'd天h时i分') {
    $diff = strtotime($time2) - strtotime($time1);
    $date['d'] = floor($diff / 86400);
    $timepart = $diff % 86400;
    $date['h'] = floor($timepart / 3600);
    $date['H'] = $date['h'] < 10 ? '0' . $date['h'] : $date['h'];
    $date['i'] = floor($timepart % 3600 / 60);
    $date['I'] = $date['i'] < 10 ? '0' . $date['i'] : $date['i'];
    $date['s'] = floor($timepart % 60);
    $date['S'] = $date['s'] < 10 ? '0' . $date['s'] : $date['s'];
    foreach ($date as $key => $value) {
        $format = str_replace($key, $value, $format);
    }
    return $format;
}

/**
 * 导出数组到Excel文件
 * <br />例如：
 * $exconf = array('filename' => '会员账户列表-' . date('mdHi'),  'header' => ['id' => '会员ID', 'nick_name' => '会员名称'], 'default_replace' => ['qrcode_promoter_login_name' => ''],];
 * xsfn_excel_export($list, $exportconf);
 * @param array $list 数据集
 * @param array $options 更多选项,包含 string:filename, string:sheetname, int:activesheet, string:creator, array:header, array:default_replace
 * @version 1.0 2014-9-5 by sutroon
 * @version 1.1 2015-4-10 SoChishun 修正导出大数值自动变为科学计数的问题
 * @version 1.2 2016-7-18 SoChishun 修复在部分服务器中导出的XLS提示不能识别的文件格式的问题
 * @version 1.3 2018-1-24 SoChishun 选项名称由大驼峰命名法改为全小写,新增head和default_replace选项
 * @example xsfn_excel_export($list);
 */
function xsfn_excel_export($list, $options = array()) {
    $defopt = array(
        'filename' => 'excel-' . date('mdHi'), // 文件名
        'sheetname' => 'Sheet1', // 表格名称
        'activesheet' => 0, // 活动表格序号
        'SaveTemp' => false,
        'creator' => 'xwam', // 署名
        'header' => array(), // 自定义标题栏, 字段数量必需与查询字段完全一致, 有多少个字段就输出多少个字段
        'default_replace' => array(), // 默认值替换, 用于解决header有的字段，而row没有，则会显示header的标题值
    );
    if ($options) {
        $options = array_merge($defopt, $options);
    } else {
        $options = $defopt;
    }
    //vendor('PHPExcel.PHPExcel'); // TP3写法    
    //think\Loader::import('PHPExcel.PHPExcel'); // TP5写法
    //think\Loader::autoload('PHPExcel'); // TP5.1写法
    $objPHPExcel = new \PHPExcel();
    $objPHPExcel->getProperties()->setCreator($options['creator'])
            ->setLastModifiedBy("PHPExcel")
            ->setTitle("Office 2007 XLSX Test Document")
            ->setSubject("Office 2007 XLSX Test Document")
            ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
            ->setKeywords("office 2007 openxml php")
            ->setCategory("Test result file");
    $sheet = $objPHPExcel->getActiveSheet($options['activesheet']);
    $i = 1;
    $hasHeader = empty($options['header']) === false; // 是否自定义标题栏
    $hasDefault = empty($options['default_replace']) === false; // 是否替换默认值
    foreach ($list as $row) {
        if ($i == 1) {
            $c = 0;
            $header = $hasHeader ? $options['header'] : array_keys($row);
            foreach ($header as $k) {
                $sheet->setCellValueByColumnAndRow($c, $i, $k);
                $c++;
            }
        }
        $i++;
        $c = 0;
        if ($hasHeader) {
            $datarow = array_merge($options['header'], $row); // 合并覆盖字段值
            $datarow = array_intersect_key($datarow, $options['header']); // 裁剪字段,只读取自定义标题栏定义的字段
            if ($hasDefault) { // 默认值替换, 用于解决header有的字段，而row没有，则会显示header的标题值
                foreach ($options['default_replace'] as $key => $val) {
                    if (isset($datarow[$key])) {
                        $datarow[$key] = $val;
                    }
                }
            }
        } else {
            $datarow = $row;
        }
        foreach ($datarow as $k => $v) {
            //$sheet->setCellValueByColumnAndRow($c, $i, $v);
            $sheet->setCellValueExplicitByColumnAndRow($c, $i, $v, \PHPExcel_Cell_DataType::TYPE_STRING);
            $c++;
        }
    }
    $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddHeader('&L&G&C&HPlease treat this document as confidential!');
    $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddFooter('&L&B' . $objPHPExcel->getProperties()->getTitle() . '&RPage &P of &N');
    $objPHPExcel->getActiveSheet()->setTitle($options['sheetname']);
    $filename = $options['filename'] . '.xlsx';
    //$filename=  iconv('UTF-8', 'ISO_8859_1', $filename);
    ob_end_clean(); // 导出的XLS提示不能识别的文件格式(see http://www.dreamgoo.com/archives791.html)
    //header('Content-Type: application/vnd.ms-excel');
    header('Content-Type: application/octet-stream');
    header("Content-Disposition: attachment;filename=$filename");
    header('Cache-Control: max-age=0');
    $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
    $objWriter->save('php://output');
    exit(0);
}

/**
 * 导出文件
 * @param string $filename
 * @version 1.0 <2015-9-21> SoChishun Added.
 */
function xsfn_file_export($filename) {
    header('Content-Type: application/octet-stream');
    header("Content-Disposition: attachment;filename=$filename");
    header('Cache-Control: max-age=0');
}

/**
 * 强制弹出保存文件对话框
 * @param string $default_file_name 对话框中默认文件名
 * @param string $content 要保存的内容
 * @param bool $is_path 指示$content参数是内容还是文件路径,默认false
 * @version 1.0 2014-10-11 by sutroon; 1.1 2015-1-5 by sutroon 新增路径支持功能
 * @example xsfn_save_file_dialog('image1.jpg',file_get_contents($path));
 */
function xsfn_save_file_dialog($default_file_name, $content, $is_path = false) {
    if ($is_path) {
        if (file_exists($content)) {
            //打开文件  
            $file = fopen($content, "r");
            //输入文件标签   
            Header("Content-type: application/octet-stream");
            Header("Accept-Ranges: bytes");
            Header("Accept-Length: " . filesize($content));
            Header("Content-Disposition: attachment; filename=" . $default_file_name);
            //读取文件内容并直接输出到浏览器  
            echo fread($file, filesize($content));
            fclose($file);
            exit();
        } else {
            die('抱歉, 服务器上文件已删除或更名, 请联系管理员!');
        }
    } else {
        if (false === $content) {
            die('抱歉, 服务器上文件已删除或更名, 请联系管理员!');
        }
        header("Content-Type: application/force-download");
        header('Content-Disposition: attachment; filename="' . $default_file_name . '"');
        echo $content;
        exit(0);
    }
}

/**
 * 将Excel文件转为数组
 * @param string $path Excel文件的服务器路径
 * @param array $options 更多选项,包含 int:START, int:COUNT, int:ActiveSheet
 * @return mixed 如果文件存在则返回array,否则返回false,数组键是标题名称
 * @version 1.0 2014-9-5 by sutroon
 * @version 1.1 2015-4-29 SoChishun 新增ONLY_GET_COUNT参数用于统计
 * @example $arr_content = xsfn_excel_import(UPLOAD_ROOT . $msg['filePath']);
 */
function xsfn_excel_import($path, $options = array()) {
    $options = array_merge(array(
        'start' => 2, // 起始数据行,默认2(第一行为标题)
        'count' => 0, // 数量,0=不限
        'activesheet' => 0, // 页面索引,默认0
        'only_get_count' => false, // 是否只返回数量而不是数据数组(用于数量判断)
            ), is_array($options) ? $options : array());
    if (!file_exists($path)) {
        return false;
    }
    //vendor('PHPExcel.PHPExcel'); // TP3写法    
    think\Loader::import('PHPExcel.PHPExcel'); // TP5写法
    $objPHPExcel = \PHPExcel_IOFactory::load($path);
    $sheet = $objPHPExcel->getActiveSheet($options['activesheet']);
    $highestRow = $sheet->getHighestRow(); // 取得总行数,数值,如:30
    $highestColumnName = $sheet->getHighestColumn(); // 取得总列数,字符,如:F
    $highestColumn = \PHPExcel_Cell::columnIndexFromString($highestColumnName); // 转换成总列数数值
    if ($options['only_get_count']) { // 如果只是返回数量统计,则到此退出,提高效率
        return array('rows' => $highestRow, 'cols' => $highestColumn);
    }
    $aout = array();
    $i = $options['start'];
    $count = $options['count'];
    while ($i <= $highestRow) {
        if ($count > 0 && ($i - 2) > $count) {
            break;
        }
        for ($c = 0; $c < $highestColumn; $c++) {
            $title = $sheet->getCellByColumnAndRow($c, 1)->getValue(); // 格式化标题行
            if ($title instanceof \PHPExcel_RichText) { //富文本转换字符串
                $title = $title->__toString();
            }
            $cell = $sheet->getCellByColumnAndRow($c, $i);
            $value = $cell->getValue();
            if ($value instanceof \PHPExcel_RichText) { //富文本转换字符串
                $value = $value->__toString();
            }
            if ($cell->getDataType() == \PHPExcel_Cell_DataType::TYPE_NUMERIC) { // 格式化日期
                $cellstyleformat = $cell->getStyle()->getNumberFormat();
                $formatcode = $cellstyleformat->getFormatCode();
                if (preg_match('/^(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy]/i', $formatcode)) {
                    $value = gmdate("Y-m-d", \PHPExcel_Shared_Date::ExcelToPHP($value));
                } else {
                    $value = \PHPExcel_Style_NumberFormat::toFormattedString($value, $formatcode);
                }
            }
            $aout[$i - 2][$title] = $value;
        }
        $i++;
    }
    return $aout;
}

/**
 * 生成select的option元素
 * <p>
 * xsfn_combobox_option(array('EQ'=>'等于','GT'=>'大于','LT'=>'小于'),$search['operate_date_start'],'EQ')
 * </p>
 * @param array $aitems
 * @param mixed $value
 * @param mixed $default
 * @return string
 * @version 1.0 2016-5-27
 */
function xsfn_combobox_option($aitems, $value, $default) {
    $outstr = '';
    if (!$value) {
        $value = $default;
    }
    foreach ($aitems as $id => $text) {
        $outstr .= '<option value="' . $id . '"' . ($value && $value == $id ? ' selected="selected"' : '') . '>' . $text . '</option>';
    }
    return $outstr;
}

/**
 * 获取服务端IP地址
 * @return string
 * @version 1.0 2016-7-1 SoChishun Added.
 */
function get_host_ip() {
    return isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
}

/**
 * 获取客户端IP地址
 * <br />来源：ThinkPHP
 * <br />"X-FORWARDED-FOR" 是代理服务器通过 HTTP Headers 提供的客户端IP。代理服务器可以伪造任何IP。
 * <br />要防止伪造，不要读这个IP即可（同时告诉用户不要用HTTP 代理）。
 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
 * @param boolean $adv 是否进行高级模式获取（有可能被伪装） 
 * @return mixed
 */
function get_client_ip($type = 0, $adv = false) {
    $type = $type ? 1 : 0;
    static $ip = NULL;
    if ($ip !== NULL)
        return $ip[$type];
    if ($adv) {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos = array_search('unknown', $arr);
            if (false !== $pos)
                unset($arr[$pos]);
            $ip = trim($arr[0]);
        }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
    } elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    // IP地址合法验证, 防止通过IP注入攻击
    $long = sprintf("%u", ip2long($ip));
    $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
    return $ip[$type];
}

/**
 * 格式化消息
 * @param boolean $status 状态
 * @param mixed $message 消息 {%}
 * @param type $implode
 * @return array
 * @version 1.0 2017-1-2 SoChishun Added.
 */
function xsfn_message($status, $message = '操作{%}', $implode = false) {
    if (is_array($message)) {
        if (true === $implode) {
            $implode = '<br />';
        }
        if ($implode) {
            $message = implode($implode, $message);
        }
    } else {
        $message = str_replace('{%}', ($status ? '成功' : '失败'), $message);
    }
    return array('status' => $status, 'info' => $message);
}

/**
 * 
 * @param type $message
 * @param type $implode
 * @return type
 * @version 1.0 2017-3-13 SoChishun Added.
 */
function xsfn_message_format($message, $implode = '<br />') {
    if (isset($message['info']) && is_array($message['info'])) {
        $message = implode($implode, $message['info']);
    }
    return $message;
}

/**
 * 检测系统环境
 * @version 1.0 <2015-9-28> SoChishun Added.
 */
function xsfn_check_env() {
    version_compare(PHP_VERSION, '5.3.0', '<') and exit('系统需要PHP版本5.3.0以上环境(当前版本:' . PHP_VERSION . ').');
    function_exists('mysql_close') or exit('系统需要MySQL环境支持.');
    $ver = '';
    if (function_exists("mysql_get_server_info")) {
        $ver = mysql_get_server_info();
    }
    if (!$ver) {
        $result = $this->query('select VERSION()');
        $ver = current($result[0]);
    }
    version_compare($ver, '5.1.38', '<') and exit('系统需要MySQL版本5.1.0以上环境(当前版本:' . $ver . ').');
}

/**
 * 生成序列号
 * <br />把12位时间数值压缩成7-8位字母+数字组合字符串,重点是加上用户编号后将永不重复哈
 * <br />用法：$serial_no = sofn_generate_serial('KH' . $this->user_login_data['id']);
 * <br />示例：160121054346(12位,date('ymdhis'))压缩后QBVF4346(8位,sofn_generate_serial())
 * @param string $serial_no 序号前缀，如:'KH' . $this->user_login_data['id']
 * @return string 如：QBVF295
 * @version VER:1.0; DATE:2016-1-21; AUTHOR:SoChishun; EMAIL:14507247@qq.com; DESC:Added.
 */
function xsfn_generate_serial($serial_no = '') {
    $time = date('y-m-d-h-i-s'); // 16-01-22-05-26-31
    $atime = explode('-', $time);
    foreach ($atime as $stime) {
        $itime = $stime * 1;
        if ($itime < 26) {
            // 65(A)-90(Z)
            $serial_no .= chr(65 + $itime);
            continue;
        }
        // 48(0)-57(9)
        if ($itime >= 48 && $itime <= 57) {
            $serial_no .= chr($stime);
            continue;
        }
        $serial_no .= $stime;
    }
    return $serial_no;
}

// 2017-3-16
function xsfn_array_notempty($data, $keys) {
    if ($keys) {
        foreach ($keys as $key) {
            if (isset($data[$key]) && empty($data[$key])) {
                unset($data[$key]);
            }
        }
    } else {
        foreach ($data as $key => $value) {
            if (empty($value)) {
                unset($data[$key]);
            }
        }
    }
    return $data;
}

/**
 * 将路径中多个连续的/符号替换成一个/符号
 * <br />例如：/path1///path2//path3/  /path4/ 替换后是 /path1/path2/path3/path4/
 * @param string $path
 * @return string
 * @version 2018-1-24
 */
function xsfn_directory_separator_filter($path) {
    $path = preg_replace('/[\/]+/', '/', $path); // 替换连续斜杠 /// 为单个斜杠 /
    $path = preg_replace('/\/[\s]+\//', '/', $a); // 替换 /  /这种两个斜杠中间有空格的为单个斜杠 /
    return $path;
}

/**
 * SQL注入代码检测
 * @param type $request
 * @version 2018-4-18
 */
function xsfn_check_sql_injection($request) {
    $pattern = "/(select[\s])|(insert[\s])|(update[\s])|(delete[\s])|(drop[\s])|(truncate[\s])|([\s]from[\s])|([\s]where[\s])/i";
    foreach ($request as $k => $v) {
        if (preg_match($pattern, $v, $match)) {
            die("SQL Injection denied!");
        }

        if (is_array($v)) {
            filter_injection($request[$k]);
        } else {
            if (preg_match($pattern, $v, $match)) {
                die("SQL Injection denied!");
            }
        }
    }
}

// 2018-4-21
function xsfn_html_combobox($name, $data, $value, $is_required = false, $attr = 'class="form-control input-sm"') {
    $data = is_array($data) ?: json_decode($data, true);
    if (!$data) {
        return '';
    }
    $outstr = '<select name="' . $name . '"' . ($is_required ? ' required="required"' : '') . ' ' . $attr . '>';
    foreach ($data as $row) {
        $outstr .= '<option value="' . $row['id'] . '"' . (($value && $value == $row['id']) ? ' selected="selected"' : '') . '>' . $row['text'] . '</option>';
    }
    $outstr .= '</select>';
    return $outstr;
}
